<script setup>
import Loading from '@/views/study/three/components/loading.vue'
import { getElementSize } from '@/views/study/three/composables'
import { useResizeObserver } from '@vueuse/core'
import * as THREE from 'three'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js'

const webgl = ref(null)

const progressValue = ref(0)
const progressShow = ref(true)

let renderer = null
let camera = null
let mixer = null

onMounted(() => {
  const { width, height } = getElementSize(webgl)

  const scene = new THREE.Scene()
  const clock = new THREE.Clock()
  camera = new THREE.PerspectiveCamera(45, width / height, 0.1, 1000)

  camera.position.set(0, 0, 5)

  const loader = new GLTFLoader()
  loader.load(new URL('@/assets/model/dragon.glb', import.meta.url).href, (obj) => {
    const model = obj.scene

    // 遍历设置模型本身的材质颜色和贴图（自发光）
    model.traverse((child) => {
      // 是否是网格材质
      if (child.isMesh) {
        // 设置自发光为材质的颜色
        child.material.emissive = child.material.color
        // 设置自发光贴图为材质的贴图
        child.material.emissiveMap = child.material.map
      }
    })

    // 播放动画
    mixer = new THREE.AnimationMixer(model)
    obj.animations.forEach((clip) => {
      mixer.clipAction(clip).play()
    })

    progressShow.value = false

    scene.add(model)
  }, ({ loaded, total }) => {
    progressValue.value = (loaded / total) * 100
  })

  renderer = new THREE.WebGLRenderer({
    antialias: true,
  })
  renderer.setClearColor(0, 0)
  renderer.setSize(width, height)
  renderer.render(scene, camera)
  webgl.value.appendChild(renderer.domElement)

  const orbit = new OrbitControls(camera, renderer.domElement)
  orbit.enableDamping = true

  function render() {
    requestAnimationFrame(render)

    // 更新动画状态
    if (mixer)
      mixer.update(clock.getDelta())

    orbit.update()

    renderer.render(scene, camera)
  }

  render()
})

function resizeHandler() {
  const { width, height } = getElementSize(webgl)

  camera.aspect = width / height
  camera.updateProjectionMatrix()

  renderer.setSize(width, height)
}

useResizeObserver(webgl, () => {
  resizeHandler()
})
</script>

<template>
  <n-el>
    <div ref="webgl" bg="$base-color" rd="$border-radius" relative wh="full">
      <Loading :show="progressShow" :width="progressValue" />
    </div>
  </n-el>
</template>
